package com.zbkj.admin.service.impl;

import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zbkj.admin.service.AdminInvoiceHandlerService;
import com.zbkj.common.config.BaseConfig;
import com.zbkj.common.constants.Constants;
import com.zbkj.common.exception.CommonException;
import com.zbkj.common.invoice.Invoice;
import com.zbkj.common.invoice.InvoiceToExcel;
import com.zbkj.common.invoice.PdfInvoiceExtractor;
import com.zbkj.common.invoice.PeInvoiceApply;
import com.zbkj.common.model.order.OrderInfo;
import com.zbkj.common.model.user.User;
import com.zbkj.common.oss.OssClientUtil;
import com.zbkj.common.utils.CommonUtil;
import com.zbkj.common.utils.DateUtil;
import com.zbkj.common.utils.LocalDateUtil;
import com.zbkj.common.utils.UploadUtil;
import com.zbkj.service.service.invoice.IPeInvoiceApplyService;
import com.zbkj.service.service.order.OrderInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Auther:
 * @Date: 2024/8/23 16:47
 * @Description:
 */
@Slf4j
@Service
public class AdminInvoiceHandlerServiceImpl implements AdminInvoiceHandlerService {
    @Autowired
    private BaseConfig baseConfig;

    @Autowired
    private OssClientUtil ossClientUtil;

    @Autowired
    private IPeInvoiceApplyService peInvoiceApplyService;

    @Autowired
    private OrderInfoService orderInfoService;

    @Override
    public Map<String,String> invoicePdfParse(List<MultipartFile> files) {
        Map<String,String> resultMap = Maps.newHashMap();
        invoceFilesCheck(files);
        List<Invoice> invoicList = Lists.newArrayList();
        List<String> errorList = Lists.newArrayList();
        //最多5个线程进行发票文件解析
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        List<CompletableFuture<Integer>> featureList = Lists.newArrayList();
        for(MultipartFile mfile : files) {
            CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()->{
                invoicPdfToPojo(mfile, invoicList,errorList);
                return 1;
            },executorService).exceptionally(ce->{
                throw new CommonException(ce.getMessage());
            });
            featureList.add(future);
        }
        CompletableFuture allFuture = CompletableFuture.allOf(featureList.toArray(new CompletableFuture[featureList.size()]));
        try {
            allFuture.join();
            //executorService.shutdown();
        } catch (Exception e) {
            throw new CommonException("发票文件解析失败");
        } finally {
            executorService.shutdown();
        }
        if(CollectionUtils.isNotEmpty(errorList)) {
            throw new CommonException(errorList.get(0));
        }
        if(CollectionUtils.isEmpty(invoicList)) {
            throw new CommonException("发票文件解析失败");
        }
        File excelFile = null;
        try {
            //解析出发票数据写入excel
            excelFile = invoceWriteExcel(invoicList);
            if(Objects.isNull(excelFile) || !excelFile.exists()) {
                throw new CommonException("发票数据生成excel文件失败");
            }
            //excel上次到oss
            String downloadUrl = invocieExcelUpload(excelFile);
            resultMap.put("downloadUrl",downloadUrl);
        } catch (CommonException ce) {
            throw ce;
        } finally {
            if(Objects.nonNull(excelFile) && excelFile.exists()) {
                excelFile.delete();
            }
        }
        return resultMap;
    }

    public void invoceFilesCheck(List<MultipartFile> files) {
        if(CollectionUtils.isEmpty(files)) {
            throw new CommonException("请上传发票文件");
        }
        for (MultipartFile file : files) {
            if(file.isEmpty()) {
                throw new CommonException("存在空发票文件");
            }
            String fname = file.getOriginalFilename();
            String fType = FilenameUtils.getExtension(fname);
            if(!CommonUtil.fileTypeCheck("pdf",fType)) {
                throw new CommonException("存在非pdf格式文件["+fname+"]");
            }
        }
    }

    private void invoicPdfToPojo(MultipartFile mfile, List<Invoice> invoicList, List<String> errorList) {

        String originalFileName = mfile.getOriginalFilename();
        File dest = null;
        try {
            String ftype = FilenameUtils.getExtension(originalFileName);
            String destPath = String.format("%s%s/%s/%s.%s", baseConfig.getImagePath(),"invoice",DateUtil.nowDate(),CommonUtil.getUuid(),ftype);
            dest = UploadUtil.createFile(FilenameUtils.separatorsToSystem(destPath));
            mfile.transferTo(dest);
            Invoice invoice = PdfInvoiceExtractor.extract(dest);
            if(Objects.nonNull(invoice)) {
                invoicList.add(invoice);
            }
        } catch (Exception e) {
            log.error("invoicPdfToPojo-file:{}-解析error:",originalFileName,e);
            errorList.add(originalFileName+"解析失败");
            //throw new CommonException(originalFileName+"解析失败");
        } finally {
            if(Objects.nonNull(dest) && dest.exists()) {
                dest.delete();
            }
        }
    }

    private File invoceWriteExcel(List<Invoice> invoicList) {
        try {
            //自定义标题别名
            LinkedHashMap<String, String> aliasMap = new LinkedHashMap<>();
            aliasMap.put("name", "发票名称");
            aliasMap.put("count", "数量");
            aliasMap.put("model", "规格");
            aliasMap.put("unit", "单位");
            aliasMap.put("price", "单价");
            aliasMap.put("amount", "金额");
            aliasMap.put("taxRate", "税率");
            aliasMap.put("taxAmount", "税额");
            aliasMap.put("title", "发票类型");
            aliasMap.put("code", "发票代码");
            aliasMap.put("number", "发票号码");
            aliasMap.put("date", "开票日期");
            aliasMap.put("buyerName", "购买方名称");
            aliasMap.put("sellerName", "购买方编号");
            aliasMap.put("totalAmount", "合计");
            aliasMap.put("totalTaxAmount", "合计税额");
            aliasMap.put("totalptAmount", "价税合计");
            String localExcelPath = String.format("%s%s/%s/%s.xlsx", baseConfig.getImagePath(),"invexcel",DateUtil.nowDate(),CommonUtil.getUuid());
            ExcelWriter excelWriter = ewCreateAndSetting(localExcelPath);
            //自定义标题别名
            aliasMap.forEach((key, value) -> excelWriter.addHeaderAlias(key, value));
            excelWriter.setOnlyAlias(true);
            for (Invoice invoice : invoicList) {
                List<InvoiceToExcel> tempLisrt =InvoiceToExcel.invoiceToExcelList(invoice);
                excelWriter.write(tempLisrt);
                excelWriter.passCurrentRow();
            }
            // 关闭writer，释放内存
            excelWriter.close();
            return UploadUtil.createFile(FilenameUtils.separatorsToSystem(localExcelPath));
        } catch (Exception e) {
            log.error("invoicPdfToPojo-excel写入error:",e);
            throw new CommonException("发票数据生成excel文件失败");
        }
    }

    private ExcelWriter ewCreateAndSetting(String filePath) {
        ExcelWriter writer = ExcelUtil.getWriter(filePath);
        CellStyle headCellStyle = writer.getHeadCellStyle();
        Font font = writer.createFont();
        font.setBold(true);
        headCellStyle.setFont(font);
        //设置宽度自适应
        writer.setColumnWidth(-1, 22);
        return writer;
    }


    private String invocieExcelUpload(File excelFile) {
        String datePathStr = LocalDateUtil.formater(LocalDateTime.now(),LocalDateUtil.YYYYMMDD);
        String storagePath = String.format("%s/%s/pp%s_%s.xlsx", Constants.INVOICE_DOC_PREFIX,datePathStr,datePathStr,CommonUtil.getUuid());
        return ossClientUtil.upload(storagePath,excelFile);
    }

    @Transactional
    @Override
    public boolean invoicePush(String invoiceId) {
        PeInvoiceApply peInvoiceApply = peInvoiceApplyService.getById(invoiceId);
        peInvoiceApply.setInvoiceFlag(1);
        peInvoiceApplyService.updateById(peInvoiceApply);
        OrderInfo orderInfo = orderInfoService.getById(peInvoiceApply.getOrderId());
        orderInfo.setInvoiceFlag(1);
        return true;
    }
}
